/**
 * @file addr.c
 * Implementation of the <b>Addr<b/> class.
 */

#define _BSD_SOURCE // avoid compilation warnings which appear when used in combination with -std=c99

#include <stdlib.h>
#include <string.h>

#include "addr.h"
#include "common.h"

/** 
 * @brief Default constructor.
 *
 * @return the new object
 */
Addr *addr_new()
{
	Addr *a = (Addr *)malloc(sizeof(Addr));
	if(!a)
		return NULL;
	memset(a, 0, sizeof(Addr));
	return a;
}

/** 
 * @brief Destructor.
 *
 * @param addr the Addr object to delete
 */
void addr_delete(Addr **addr)
{
	free(*addr);
	*addr = NULL;
}

/** 
 * @brief Copy an Addr object
 *
 * @param dst destination Addr
 * @param src source Addr
 */
void addr_cpy(Addr *dst, const Addr *src)
{
	if(!src || !dst || dst == src)
		return;
	memcpy(dst, src, sizeof(Addr));
}

/**
 * @brief Copy constructor (duplicate an Addr object)
 *
 * @addr the Addr to copy
 */
Addr *addr_dup(const Addr *addr)
{
	Addr *a = addr_new();
	if(!a)
		return NULL;
	addr_cpy(a, addr);
	return a;
}

/** 
 * @brief Initialize an Addr object
 *
 * @param the given Addr
 * @param ip_str the IP address given as a string (e.g.: "127.0.0.1")
 * @param tcp_port the TCP port
 * @param udp_port the UDP port
 *
 * @return 
 * 	an Addr pointer
 * 	NULL if the address couldn't be assigned
 * This function initializes an addr. If addr is NULL, a new Addr is created.
 */
Addr *addr_set(Addr *addr, const char *ip_str, int tcp_port, int udp_port)
{
	if(!ip_str)
		return NULL;
	// Create addr if it is NULL
	if(!addr && !(addr = addr_new()))
		return NULL;
	if(inet_aton(ip_str, &addr->ip) == 0) {
		addr_delete(&addr); //FIXME delete only if it's us who created it
		return NULL;
	}
	addr->tcp_port = htons(tcp_port);
	addr->udp_port = htons(udp_port);
	return addr;
}

int _delim(int c)
{
	return c == ':';
}

// FIXME what the hell do you do with @p addr ?
Addr *addr_set_str(Addr *addr, const char *str)
{
	if(!str)
		return NULL;
	/* Split the string into ip, tcp_port and udp_port */
	int T;
	char **tok;
	if((T = get_tokens(str, &tok, _delim)) != 3)
		return NULL;
	/* Parse the contents */
	char ip[16];
	int tcp_port;
	int udp_port;
	sscanf(tok[0], "%15s", ip);
	sscanf(tok[1], "%d", &tcp_port);
	sscanf(tok[2], "%d", &udp_port);
	free_tokens(T, tok);
	/* Set the address */
	return(addr_set(addr, ip, tcp_port, udp_port));
}

/** 
 * @brief Compare two Addr objects
 * @param addr1 the first object to compare
 * @param addr2 the second object to compare
 *
 * @return
 * 	0 if the objects are equal
 * 	!= 0 if the objects are different
 */
int addr_cmp(const Addr *addr1, const Addr *addr2)
{
	if(addr1 == addr2)
		return 0;
	if(!addr1 || !addr2)
		return -1;
	return (addr1->ip.s_addr == addr2->ip.s_addr &&
		addr1->tcp_port == addr2->tcp_port &&
		addr1->udp_port == addr2->udp_port)? 0 : 1;
}

/**
 * The string is returned in a statically allocated buffer which subsequent calls will overwrite (see inet_ntoa).
 */
char *addr_get_ip(const Addr *addr)
{
	if(!addr)
		return NULL;
	return inet_ntoa(addr->ip);
}

int addr_get_tcp_port(const Addr *addr)
{
	if(!addr)
		return 0;
	return ntohs(addr->tcp_port);
}

int addr_get_udp_port(const Addr *addr)
{
	if(!addr)
		return 0;
	return ntohs(addr->udp_port);
}

char *addr_get_str(const Addr *addr)
{
	static char buf[15+1+5+1+5+1];
	sprintf(buf, "%s:%d:%d",
			addr_get_ip(addr),
			addr_get_tcp_port(addr),
			addr_get_udp_port(addr));
	return buf;
}

int addr_is_bcast(const Addr *addr)
{
	Addr a;
	addr_set(&a, "255.255.255.255", 0, 0);
	return addr_cmp(addr, &a) == 0;
}

const Addr *addr_bcast()
{
	static Addr bcast;
	addr_set(&bcast, "255.255.255.255", 0, 0);
	return &bcast;
}

void addr_read(Addr *addr, const char *str)
{
	if(!str || !addr)
		return;
	memcpy(addr, str, sizeof(Addr));
}

void addr_write(const Addr *addr, char *str)
{
	if(!str || !addr)
		return;
	memcpy(str, addr, sizeof(Addr));
}
